Copyright (c) 1991-1993 Borland International, Inc. All Rights Reserved. CALLING DLL FUNCTIONS AND THE WINDOWS API ----------------------------------------- Dynamic-link libraries (DLLs) are files containing compiled program code that Windows applications load and execute at runtime. Through DLLs, your Bladerunner application can call non-dBASE functions, such as C and Pascal functions. The Windows API (application programming interface) is a library of C functions contained in DLLs. This document introduces dynamic linking to dBASE programmers and shows how to call non- dBASE functions from DLLs and the Windows API. Contents -------- I. About Libraries and Linking II. Calling DLL Functions III. Calling Windows API Functions About Libraries and Linking --------------------------- The terms "library" and "linking" mean different things to different programmers. dBASE III PLUS developers maintain libraries of often-used routines and incorporate them into applications without linking. dBASE IV developers use DBLINK to link compiled programs and library routines into one large object file. C language developers perform "static linking" when they create an executable file from various object modules and run-time libraries. Static linking occurs just once at the time you create an application. In contrast, Windows applications perform "dynamic linking" by calling routines and resources at run time. Dynamic linking occurs many times while an application is running. Since DLLs are parts of applications, they must be present on the disk when a program calls one of their routines or resources. By maintaining compiled routines and resources in separate files, and linking them to applications at run time: o You can share the same library among many applications, even among two applications running concurrently in Windows 386 enhanced mode. o You don't have to recompile or relink the library every time you build your application. o You create smaller executable files. Calling DLL Functions --------------------- Using DLLs is much like using procedure files. You load a DLL into memory before using one of its routines, then release the DLL when its routines are no longer needed. If the DLL file has a .DLL extension, or is part of the WIndows API, Bladerunner loads the file automatically; otherwise you need to explicitly load the file with LOAD DLL. (LOAD DLL is not implemented in this Alpha release; you can use the Windows API function LoadLibrary() instead.) Since DLLs are loaded at runtime, DLL files must be present on disk at runtime. You need to declare a "prototype" for each non-dBASE function before you call the function. A prototype specifies the data type for the function's parameters and return value. You declare prototypes with the EXTERN command. The need for prototypes becomes apparent when you consider the problem of passing parameters into functions of different languages. Bladerunner uses prototypes to convert the data types of parameters and return values automatically. For instance, when you call a C function, Bladerunner automatically converts the parameters from dBASE data types into C data types, and then converts the return values from C back into dBASE. Follow these steps to call DLL functions: 1. If the DLL file does not have a .DLL extension, and is not part of the Windows API, you need to load the DLL file into memory with LOAD DLL. (Note: In this Alpha release, LOAD DLL is not yet implemented. You can use the Windows API function LoadLibrary() instead.) If the DLL file has a .DLL extension, you can skip this step. 2. Prototype the non-dBASE functions with EXTERN. 3. Call the non-dBASE functions right in your dBASE code. 4. Remove the DLL file from memory with RELEASE DLL. EXTERN prototypes non-dBASE functions. Here is the syntax: EXTERN [CDECL] ([, ...]) These are the options for EXTERN: [CDECL] -- Tells dBASE to use the C language calling convention, instead of the default Pascal calling convention, so you can pass additional parameters beyond those specified in the declaration for this function. -- Specifies the return value's data type. All EXTERN functions have to specify a return type even if it is CVOID. -- Optionally specifies the data type of each parameter. All arguments need to match their declared type as follows: Data Type Keyword API Data Type --------- ------- ------------- Numeric CWORD unsigned int (16 bit) Numeric CLONG unsigned long (32 bit) Numeric CDOUBLE double float (64 bit double) Character CPTR char far * (string) N/A CVOID void -- The name of the function in the DLL. -- The name of the Windows .DLL that contains the function. Note: Most of the functionality of Windows is contained in DLL's, but dynamic link library code can also be present in .EXE files (or even in .DRV or .FON files). Example: EXTERN CWORD MessageBox(CWORD,CPTR,CPTR,CWORD) user.exe Calling Windows API Functions ----------------------------- The API function must be prototyped before you call it from Bladerunner. You can prototype the API functions at the Command window, in the .PRG, or in an #include file. Once an API function has been prototyped, it is available as if it were an internal Bladerunner function, until you end that Bladerunner session. Execute the API function like any dBASE function after you have prototyped it. API functions can be called directly or they can be part of a dBASE expression or function. Also, dBASE functions can be put inside API functions. Example: EXTERN CWORD MessageBox(CWORD,CPTR,CPTR,CWORD) user.exe ? MessageBox(0,"My Text","From Windows",17) or pressed = MessageBox(0,"My Text","From Windows",17) or ? MessageBox(0,"My Text","From Windows",HTOI("00011")) This displays a Windows MessageBox in the middle of the screen, with the Title: "From Windows" and the Message: "My Text" and two buttons: "OK" "CANCEL" and the ICON: "STOP". It returns the value for the button pressed 1= OK , 2 = CANCEL. Note: You can execute the function without the ? (print) command, just by putting the function name with its arguments on the command line by itself, but you won't display the return value. Example: EXTERN CWORD MessageBox(CWORD,CPTR,CPTR,CWORD) user.exe MessageBox(0,"My Text","From Windows",17) Displays MessageBox as above but without displaying the return value for the button pressed. The windows API functions can be prototyped and executed right at the command window in Bladerunner. You do not have to load any special header file or .DLL to access the windows API functions. API Window Handles API window handles are numbers, 16 bit integers (CWORD), like file handles in DOS, that are assigned to every window. To get the Windows handle of dBASEWIN.EXE you can use the following approaches: Example A: hDBW = _APP.FRAMEWIN.HWND The variable hDBW now holds the Windows handle to dBASEWIN.EXE FRAMEWIN (the main window of dBASEWIN.EXE). Example B: EXTERN CWORD GetActiveWindow( ) user.exe hDBW = GetActiveWindow( ) The variable hDBW now holds the windows handle of the active window. If you type the above in the Command window, the variable hDBW will hold the Windows handle of the FRAMEWIN of dBASEWIN.EXE, as in Example A, above. Example C: EXTERN CWORD GetFocus() user.exe hDBW = GetFocus() The variable hDBW now holds the windows handle of the window with input focus. If you type the above in the Command window, the variable hDBW will hold the Windows handle of the Command window. But if you DEFINE a window and ACTIVATE the window, then issue the GetFocus() function in your program, it will return the handle of the window you ACTIVATED. Windows API Constants Windows has many constants defined as integers, like the cursors below. Example: #define IDC_ARROW (32512) #define IDC_IBEAM (32513) #define IDC_WAIT (32514) #define IDC_CROSS (32515) #define IDC_UPARROW (32516) #define IDC_SIZE (32640) #define IDC_ICON (32641) #define IDC_SIZENWSE (32642) #define IDC_SIZENESW (32643) #define IDC_SIZEWE (32644) #define IDC_SIZENS (32645) EXTERN CWORD LoadCursor(CWORD,CWORD) user.exe EXTERN CWORD SetCursor(CWORD) user.exe orgcsr = SetCursor(LoadCursor(0, IDC_ICON)) FOR x = 1 to 210000 NEXT SetCursor(orgcsr) HEX Numbers Windows has many constants defined as hex numbers. Example: #define MB_OK 0x0000 #define MB_OKCANCEL 0x0001 #define MB_ABORTRETRYIGNORE 0x0002 #define MB_YESNOCANCEL 0x0003 #define MB_ICONHAND 0x0010 #define MB_ICONQUESTION 0x0020 #define MB_ICONEXCLAMATION 0x0030 #define MB_ICONASTERISK 0x0040 EXTERN CWORD MessageBox(CWORD,CPTR,CPTR,CWORD) user.exe MessageBox(0,"My Text","From Windows",HTOI("0x0022")) The dBASE functions for working with hex numbers are: HTOI() Takes a hex number as a string and returns an integer. ITOH() Takes an integer and returns a hex number as a string. Bits Many DLL functions interpret individual bits in an integer argument or return value. We have added several new functions to Bladerunner for working with bits. Bits are numbered in an integer from the least significant bit (rightmost) to the most significant bit (leftmost). Bit numbering starts at 0 (see example below). Most Least Significant Significant 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ Example: * gVer.prg #define HiWord(x) (bitrshift(bitand(x,4294901760),16)) #define LoWord(x) (bitand(x,65535)) #define HiByte(x) (ltrim(str(bitrshift(bitand(x,65280),8)))) #define LoByte(x) (ltrim(str(bitand(x,255)))) EXTERN CLONG GetVersion( ) krnl386.exe z=GetVersion( ) RETURN "DOS version; "+HiByte(HiWord(z))+"."+LoByte(HiWord(z))+ ; ", Windows version"+LoByte(LoWord(z))+"."+HiByte(LoWord(z)) Note: The above code example, and several others, are included with the sample code provided with this Alpha release. The new dBASE bit functions are: BitAND(), BitLshift(), BitRshift(), BitOr(), BitXor(),and BitSet(). Windows API Strings Many API functions take a string as an argument. Some API functions only want a Pointer (a reference) to a string that the function can fill with the returned information. In this case you need to make the string large enough to hold the result (some also require you to pass the length of the string as a argument). Example: dBWhand = _APP.FRAMEWIN.HWND EXTERN CWORD GetWindowText(CWORD,CPTR,CWORD) user.exe winTitle = SPACE(80) && first make empty string to be filled lenTitle = GetWindowText(dBWhand,winTitle,80) ? winTitle Note: With Bladerunner's object-oriented language extensions you can get the same information with this command: ? _APP.FRAMEWIN.CAPTION